package com.auth.manage.domain.service;

import cn.hutool.core.lang.Assert;
import com.auth.manage.constant.AuthPortConstant;
import com.auth.manage.domain.entity.AuthCode;
import com.auth.manage.domain.entity.AuthPort;
import com.auth.manage.infr.exception.AuthorizationException;
import com.auth.manage.infr.exception.AuthorizationPortException;
import com.auth.manage.infr.exception.errorcode.AuthorizationErrorCode;
import com.auth.manage.infr.mapper.AuthorizationMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@RequiredArgsConstructor
@Service
public class AuthorizationService extends ServiceImpl<AuthorizationMapper, AuthCode> {
    private final AuthorizationPortService authorizationPortService;

    // 根据用户id查询授权码集合
    public List<AuthCode> getAuthCodesByUserId(Long userId) {
        List<AuthCode> list = lambdaQuery()
                .eq(AuthCode::getUserId, userId)
                .list();
        return list;
    }

    /**
     *
     * @param authCodeId 授权码id
     * @param count 要减少几个上限
     */
    // todo bind_port_limit针对的是已绑定的有效端口数吗
    // 1.bind_port_limit=n指 授权码可以绑定n个有效的端口：eg:old bind_port_limit 3, binded 3 valid, new bind_port_limit 2：将其中一个端口置于invalid,修改bind_port_limit = 2;管理端每禁用一个非法端口，用户都可以重新绑定一个新端口去使用
    // 管理端减少端口上限：
    // 2.bind_port_limit=n指 授权码可以绑定的n个有效或无效的端口：eg:old bind_port_limit 3, binded 3 valid, new bind_port_limit 2，将其中一个端口置于invalid,bind_port_limit = 3;
    @Transactional
    // todo 该接口和添加端口的接口都加分布式锁，锁同一个authCodeId
    public void deductPortLimit(Long authCodeId, Integer count) {
         // 1.查询当前授权码已绑定的端口数、当前授权码可以绑定的端口数
        Long bindedPortNum = authorizationPortService.getBindedPortNumByAuthCodeId(authCodeId);  // 授权码已绑定的有效端口数
        AuthCode authCode = getById(authCodeId);
        Long limit = authCode.getBindPortLimit();  // 当前授权码可以绑定的端口数
        // 2.如果当前已绑定的端口数大于要修改后端口上限，修改超出上限部分的端口绑定的status字段为禁用
        long nowBindedPortLimit = limit - count;  // 修改后，当前授权码可以绑定的端口上限
        //  todo 修改授权码的端口上限的逻辑，无论是否超过了已绑定的端口数，都要执行；这里要重新写一遍放在这里好，还是抽出来只执行一次就行
        if (nowBindedPortLimit >= bindedPortNum){
            boolean update = lambdaUpdate()
                    .eq(AuthCode::getId, authCodeId)
                    .eq(AuthCode::getBindPortLimit, limit)  // 乐观锁
                    .setSql("bind_port_limit = bind_port_limit - ?", count)
                    .update();
            Assert.isTrue(update, () -> new AuthorizationException(AuthorizationErrorCode.OPERATION_FAIL));
            return;
        }
        // update auth_port set status = 0 where auth_code_id = 1 order by priority limit 3
        boolean flag = authorizationPortService.lambdaUpdate()
                .set(AuthPort::getStatus, AuthPortConstant.INVALID)
                .eq(AuthPort::getStatus, AuthPortConstant.VALID)
                .eq(AuthPort::getAuthCodeId, authCodeId)
                .orderBy(true, false, AuthPort::getPriority)  // 第一个参数为true代表该条件会被运用
                .last("LIMIT " + (bindedPortNum - nowBindedPortLimit))
                .update();
        Assert.isTrue(flag, () -> new AuthorizationException(AuthorizationErrorCode.OPERATION_FAIL));
        // todo：
        // 3.修改授权码绑定的端口数上限

/* todo 修改成这个语句是不是可以省去乐观锁
  boolean update1 = lambdaUpdate()
                .eq(AuthCode::getId, authCodeId)
                .set(AuthCode::getBindPortLimit, nowBindedPortLimit)
                .update();*/
        boolean update = lambdaUpdate()
                .eq(AuthCode::getId, authCodeId)
                .eq(AuthCode::getBindPortLimit, limit)  // 乐观锁
                .setSql("bind_port_limit = bind_port_limit - ?", count)
                .update();
        Assert.isTrue(update, () -> new AuthorizationException(AuthorizationErrorCode.OPERATION_FAIL));
    }

    // 根据cdk获取授权码对象
    public AuthCode getAuthCodeByCdk(String cdk) {
        return  lambdaQuery()
                .eq(AuthCode::getCdk, cdk)
                .one();
    }

    // 根据cdk获取授权码的端口上限
    public Long getAuthCodeLimitByCdk(String cdk) {
        return getObj(
                lambdaQuery()
                        .select(AuthCode::getBindPortLimit)
                        .eq(AuthCode::getCdk, cdk),
                obj -> (Long) obj  // 将查询到的Object结果转换为你想要的类型
        );
    }

    // 解绑授权码对应ip和端口
    public void unbindAuthCodes(List<Long> authCodeIds) {
        boolean update = authorizationPortService.unBindIpAndPortsByAuthCodeIds(authCodeIds);
        Assert.isTrue(update, () -> new AuthorizationPortException(AuthorizationErrorCode.OPERATION_FAIL));
    }

    // 添加授权码的端口上限
    public void addPortLimit(Long authCodeId, Integer count) {
        // update auth_code set bind_port_limit = bind_port_limit + count where id = 1
        // 不需要根据authCodeId查询数据是否存在，可以直接通过执行结果判断这条sql是否成功
        AuthCode authCode = getById(authCodeId);
        boolean update = lambdaUpdate()
                .setSql("bind_port_limit = bind_port_limit + ?", count)
                .eq(AuthCode::getBindPortLimit, authCode.getBindPortLimit())  // todo 修改点
                .eq(AuthCode::getId, authCodeId)
                .update();
        Assert.isTrue(update, () -> new AuthorizationException(AuthorizationErrorCode.OPERATION_FAIL));
    }

    public Long getAuthCodeIdCdk(String cdk) {
        return lambdaQuery()
                .eq(AuthCode::getCdk, cdk)
                .select(AuthCode::getId)
                .oneOpt()
                .map(AuthCode::getId)
                .orElse(null);
    }
}
